# -*- coding: utf-8 -*-
#   __author__ = "Casey"     Email = 395280963qq.com
#   Data :2019/12/23  Python:3.7

import time, random
from PIL import Image
from io import BytesIO
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver import Chrome
from selenium.webdriver import ChromeOptions
import redis
from apscheduler.schedulers.blocking import BlockingScheduler


'''
用于天眼查自动登录，解决滑块验证问题
账户密码在最下面的 count_cookie()  函数传入
'''


def get_track(distance):
    '''
    拿到移动轨迹，模仿人的滑动行为，先匀加速后匀减速
    匀变速运动基本公式：
    ①v=v0+at
    ②s=v0t+(1/2)at²
    ③v²-v0²=2as

    :param distance: 需要移动的距离
    :return: 存放每0.2秒移动的距离
    '''
    # 初速度
    v = 0
    # 单位时间为0.2s来统计轨迹，轨迹即0.2内的位移
    t = 0.2
    # 位移/轨迹列表，列表内的一个元素代表0.2s的位移
    tracks = []
    # 当前的位移
    current = 0
    # 到达mid值开始减速
    mid = distance * 7 / 8

    distance += 10  # 先滑过一点，最后再反着滑动回来
    # a = random.randint(1,3)
    while current < distance:
        if current < mid:
            # 加速度越小，单位时间的位移越小,模拟的轨迹就越多越详细
            a = random.randint(2, 4)  # 加速运动
        else:
            a = -random.randint(3, 5)  # 减速运动

        # 初速度
        v0 = v
        # 0.2秒时间内的位移
        s = v0 * t + 0.5 * a * (t ** 2)
        # 当前的位置
        current += s
        # 添加到轨迹列表
        tracks.append(round(s))

        # 速度已经达到v,该速度作为下次的初速度
        v = v0 + a * t

    # 反着滑动到大概准确位置
    for i in range(4):
        tracks.append(-2)
    for i in range(4):
        tracks.append(-1)
    return tracks


def push_redis(Cookie, score=3):
    link = redis.StrictRedis(host='127.0.0.1', port=6379, db=0)
    link.zadd('TYCCookie', {Cookie: score})


def autologin(driver, account, password):
    driver.get('https://www.tianyancha.com/?jsid=SEM-BAIDU-PP-SY-000873&bd_vid=7864822754227867779')
    time.sleep(3)
    try:
        driver.find_element_by_xpath('//*[@id="tyc_banner_close"]').click()
    except:
        pass
    #   获取登录的焦点并点击
    driver.find_element_by_xpath('//*[@id="web-content"]/div/div[1]/div[1]/div/div/div[2]/div/div[4]/a').click()
    time.sleep(3)
    #   获取密码登录的焦点 并点击
    driver.find_element_by_xpath('//*[@class="title-tab text-center"]//div[2]').click()
    time.sleep(3)
    #   获取账户密码的输入框
    accxp = '//*[@id="mobile"]'
    pasxp = '//*[@id="password"]'
    driver.find_element_by_xpath(accxp).send_keys(account)
    time.sleep(1)
    driver.find_element_by_xpath(pasxp).send_keys(password)
    #  获取登录焦点并点击
    clixp = '//*[@class="modulein modulein1 mobile_box  f-base collapse in"]/div[2]'
    driver.find_element_by_xpath(clixp).click()
    # 点击登录之后开始截取验证码图片
    time.sleep(2)
    print('登录成功')
    img = driver.find_element_by_xpath('//div[@class="gt_cut_bg gt_show"]')
    time.sleep(0.5)
    # 获取图片左上角那个点的位子
    location = img.location
    # 获取图片的宽和高
    size = img.size
    # 返回左上角和右下角的坐标来截取图片
    top, bottom, left, right = location['y'], location['y'] + size['height'], location['x'], location['x'] + size[
        'width']
    # 截取第一张图片(无缺口的)
    screenshot = driver.get_screenshot_as_png()
    screenshot = Image.open(BytesIO(screenshot))
    captcha1 = screenshot.crop((left, top, right, bottom))
    print('--->', captcha1.size)
    captcha1.save('captcha1.png')
    # 截取第二张图片(有缺口的) 点击按钮
    driver.find_element_by_xpath('/html/body/div[10]/div[2]/div[2]/div[2]/div[2]').click()
    time.sleep(2)
    img1 = driver.find_element_by_xpath('//div[@class="gt_cut_bg gt_show"]')
    time.sleep(0.5)
    location1 = img1.location
    size1 = img1.size
    top1, bottom1, left1, right1 = location1['y'], location1['y'] + size1['height'], location1['x'], location1['x'] + \
                                   size1['width']
    screenshot = driver.get_screenshot_as_png()
    screenshot = Image.open(BytesIO(screenshot))
    captcha2 = screenshot.crop((left1, top1, right1, bottom1))
    captcha2.save('captcha2.png')
    # 获取偏移量
    left = 46  # 滑块的初始位置
    for i in range(left, captcha1.size[0]):
        for j in range(captcha1.size[1]):
            # 判断两个像素点是否相同
            pixel1 = captcha1.load()[i, j]
            pixel2 = captcha2.load()[i, j]
            threshold = 60
            if abs(pixel1[0] - pixel2[0]) < threshold and abs(pixel1[1] - pixel2[1]) < threshold and abs(
                    pixel1[2] - pixel2[2]) < threshold:
                pass
            else:
                left = i
    print('缺口位置>>>', left)
    # 减去缺口位移
    left -= 53
    print('移动的距离>>>', left)
    # 开始移动
    tracks = get_track(left)
    # offsets, tracks = easing.get_tracks(left, 12, 'ease_out_expo')
    print('滑动轨迹', tracks)
    # 得到滑块标签
    slider = driver.find_element_by_xpath('/html/body/div[10]/div[2]/div[2]/div[2]/div[2]')
    time.sleep(1)
    t = random.uniform(0, 0.5)
    # 使用click_and_hold()方法悬停在滑块上，perform()方法用于执行
    ActionChains(driver).click_and_hold(slider).perform()
    for x in tracks:
        # 使用move_by_offset()方法拖动滑块，perform()方法用于执行
        ActionChains(driver).move_by_offset(xoffset=x, yoffset=0).perform()
        # time.sleep(random.random() / 100)
    # 模拟人类对准时间
    time.sleep(t)
    # 释放滑块
    ActionChains(driver).release().perform()
    #   如果不成功重试
    time.sleep(2)
    try:
        if driver.find_element_by_xpath('/html/body/div[10]/div[2]/div[2]/div[2]/div[2]'):
            print('能找到滑块，重新试')
            driver.delete_all_cookies()
            driver.refresh()
            autologin(driver, account, password)
        else:
            print('login success')
            cookie = driver.get_cookies()
            print(cookie)
    except:
        print('login success')
        listCookies = driver.get_cookies()
        cookie = [item["name"] + "=" + item["value"] for item in listCookies]
        cookiestr = '; '.join(item for item in cookie)
        push_redis(cookiestr)
        print("写入cookie成功,cookie为>>> %s" % (cookiestr))
        driver.close()


def main(account, password):
    """     执行程序
    :param account : 账户
    :param password:  密码
    :return:
    """
    option = ChromeOptions()
    option.add_experimental_option('excludeSwitches', ['enable-automation'])
    driver = Chrome(options=option)
    driver.maximize_window()
    #  隐式等待
    driver.implicitly_wait(10)
    autologin(driver, account, password)


def count_cookie():
    """
    监控 cookie池
    :param account:
    :param password:
    :return:
    """
    account = '******'  # 账号
    password = '******'  # 密码
    link = redis.StrictRedis(host='127.0.0.1', port=6379, db=0)
    #   统计cookie数量
    _sum = link.zcard('TYCCookie')
    #   设置cookie的大小为20
    count = 20
    if _sum < count:
        main(account, password)


if __name__ == '__main__':
    #  实例化
    scheduler = BlockingScheduler()
    scheduler.add_job(count_cookie, 'cron', second='*/20')  # 这里设置检测评论，推荐2s一次(默认)  second='*/1'
    try:
        scheduler.start()
    except (KeyboardInterrupt, SystemExit):
        pass